//==============================================================================
// SearchResult.cc
// ----------------------------------------------------------------------------
//
//==============================================================================
//.......1.........2.........3.........4.........5.........6.........7.........8
//
#include "SearchResult.hh"


//extern LogStream ls;

#if 0
bool cmpByFwdRev(const SearchResult &a,const SearchResult &b){
  int idxA = a.mQueryName.find("(-)",0);
  int idxB = b.mQueryName.find("(-)",0);
  bool bARev = (idxA >= 0);
  bool bBRev = (idxB >= 0);
  
  if (bARev && !bBRev) return(true);
  else return(false);
}
#endif

bool cmpByQueryLocation(const SearchResult &a,const SearchResult &b){
	return(a.mQueryLocation<b.mQueryLocation);
}

bool cmpBySubjectLocation(const SearchResult &a,const SearchResult &b){
	return(a.mSubjectLocation<b.mSubjectLocation);
}

bool cmpByQueryIdx(const SearchResult &a,const SearchResult &b){
	return(a.mQueryIdx<b.mQueryIdx);
}

bool cmpBySubjectIdx(const SearchResult &a,const SearchResult &b){
	return(a.mSubjectIdx < b.mSubjectIdx);
}


//---------------------------------------------------------------------------
// * Print
//---------------------------------------------------------------------------
//
void
SearchResult::Print(ostream &fout)
{
  //fout<<mQueryName.substr(0,20)<<"\t";
  //fout<<mSubjectName<<"\t";
  //fout<<mScoreBits<<"\t";
  //fout<<mExpect<<"\t";
  //fout<<mIdentitiesNum<<"/"<<mIdentitiesDen<<"\t";
  fout<<mQueryIdx<<"\t";
  fout<<mQueryLocation<<"\t";
  fout<<mSubjectIdx<<"\t";
  fout<<mSubjectLocation<<endl;
}


//---------------------------------------------------------------------------
// * SearchResultVec
//--------------------------------------------------------------------------- 
//
SearchResultVec::SearchResultVec()
{

}

#if 0
//---------------------------------------------------------------------------
// * SearchResultVec
//--------------------------------------------------------------------------- 
// As a simple heuristic "longest span" is determined by the mere 
// presence of hits in the source.   
// 
void
SearchResultVec::FindLongestContiguousSpan(long &bestStart,long &bestEnd)
{
  // Sort the list into something easy to work with. 
  // First sort by location. 
  sort(this->begin(),this->end(),cmpByQueryLocation);
  
  // then sort by whether result is for a forward or reverse sample
  stable_sort(this->begin(),this->end(),cmpByFwdRev);

  //this->Print(cout);

  long newSpan = 0,bestSpan = 0;
  int startIdx = 0,endIdx = 0;
  int bestStartIdx=0;
  int bestEndIdx=0;
  
  for(int r = 0;r < (*this).size();r++){
	 // If we are looking for a start
	 if (startIdx == 0){
		if ((*this)[r].mSubjectLocation >0){
		  startIdx = r;
		}
	 }else{
		// As long as there is a match, update end to point to that latest match
		if ((*this)[r].mSubjectLocation > 0){
		  endIdx = r;
		}else{

		  // When there fails to be a match, evaluate whether the current 
		  // start-end pair is a bigger span in the query than the biggest 
		  // seen so far. 
		  newSpan = (*this)[endIdx].mQueryLocation -
			 (*this)[startIdx].mQueryLocation;
		  if (newSpan > bestSpan){ 
			 bestSpan = newSpan;
			 bestStartIdx = startIdx;
			 bestEndIdx = endIdx;
		  }
		  // Indicate that we are looking for a new start and end. 
		  startIdx= 0; 
		  endIdx= 0;
		}
	 }
  }

  // Additional test since loop may terminate without run ending any 
  // other way. 
  newSpan = (*this)[endIdx].mQueryLocation -(*this)[startIdx].mQueryLocation;
  if (newSpan > bestSpan){
	 bestStartIdx = startIdx;
	 bestEndIdx = endIdx;
  }

  bestStart = (*this)[bestStartIdx].mQueryLocation;
  bestEnd = (*this)[bestEndIdx].mQueryLocation;

}
#endif

//---------------------------------------------------------------------------
// * Print
//---------------------------------------------------------------------------
//
void
SearchResultVec::Print(ostream &fout)
{
  fout<<"Database= "<<mDatabaseName<<endl;
  fout<<"Matches\t";
  fout<<"QIdx\t";
  fout<<"QLoc\t";
  fout<<"SIdx\t";
  fout<<"SLoc"<<endl;
  fout<<"----------------------------------------------------------------";
  fout<<endl;
  for(int i = 0;i<this->size();i++){
	 (*this)[i].Print(fout);
  }
}

#if 0
//---------------------------------------------------------------------------
// * Print
//---------------------------------------------------------------------------
//
void
SearchResultVec::PrintExpectFiltered(ostream &fout,double cutoff)
{
  fout<<"Query=    "<<mQueryName<<endl;
  fout<<"Database= "<<mDatabaseName<<endl;
  for(int i = 0;i<this->size();i++){
	 if ((*this)[i].mExpect < cutoff){
		(*this)[i].Print(fout);
	 }
  }
}
#endif

//---------------------------------------------------------------------------
// * SortBySubjectIdx
//---------------------------------------------------------------------------
//
void
SearchResultVec::SortBySubjectIdx()
{
  stable_sort(this->begin(),this->end(),cmpBySubjectIdx);
}

//---------------------------------------------------------------------------
// * SortByQueryIdx
//---------------------------------------------------------------------------
//
void
SearchResultVec::SortByQueryIdx()
{
  stable_sort(this->begin(),this->end(),cmpByQueryIdx);
}

//---------------------------------------------------------------------------
// * SortBySubjectLoc
//---------------------------------------------------------------------------
//
void
SearchResultVec::SortBySubjectLoc()
{
  stable_sort(this->begin(),this->end(),cmpBySubjectLocation);
}

#if 0
//---------------------------------------------------------------------------
// * GetSubjectList
//---------------------------------------------------------------------------
// Scan through the results and build a list of subject
// 
void 
SearchResultVec::GetSubjectList(vector<string> &subjects)
{
  for(int i = 0;i<this->size();i++){
	 // See if this name is alread in query list...
	 vector<string>::iterator p;
	 p = find(subjects.begin(),subjects.end(),(*this)[i].mSubjectName);
	 // if not, add it...
	 if (p == subjects.end()){
		subjects.push_back((*this)[i].mSubjectName);
	 }
  }
}  

//---------------------------------------------------------------------------
// * GetQueryList
//---------------------------------------------------------------------------
// 
void
SearchResultVec::GetQueryList(vector<string> &queries)
{
  for(int i = 0;i<this->size();i++){
	 // See if this name is already in query list..
	 vector<string>::iterator p;
	 p = find(queries.begin(),queries.end(),(*this)[i].mQueryName);
	 // if not, add it..
	 if (p == queries.end()){
		queries.push_back((*this)[i].mQueryName);
	 }
  }
}
#endif 


//---------------------------------------------------------------------------
// * GetMaxSubjectLength
//---------------------------------------------------------------------------
//
long
SearchResultVec::GetMaxSubjectLength(SequenceVector &Sources)
{
  long maxLength = 0;
  for(int i = 0;i< Sources.size();i++){
	 if (Sources[i].size() > maxLength){
		maxLength = Sources[i].size();
	 }
  }
  return(maxLength);
}

void
SearchResultVec::ZeroArray(int *pArray,int size)
{
  for(int i = 0;i<size;i++) pArray[i]=0;
}

//---------------------------------------------------------------------------
// * PrintLocationPlot
//---------------------------------------------------------------------------
//
void
SearchResultVec::PrintLocationPlot(ostream &fout,int PlotLength,int cutoff,
											  SequenceVector &Sources)
{
  int *PlotHitBins = new int[PlotLength];

  ZeroArray(PlotHitBins,PlotLength);

  // Sort the list into something easy to work with. 
  sort(this->begin(),this->end(),cmpByQueryLocation);
  stable_sort(this->begin(),this->end(),cmpBySubjectIdx);
  
  // Only the longest subject will take up all PlotLength spaces
  // Shorter subjects will be, um, shorter. 
  long maxSubjectSize = GetMaxSubjectLength(Sources);
  double PlotLengthScale = (double)(PlotLength-1)/(double)maxSubjectSize;

  // while there are some results not plotted
  int idx = 0;
  while(idx < this->size()){
	 
	 // for each particular subject name, plot that subject.  Each subject, 
	 // being different lengths, will have it's own scaling factors.  
	 int currentSubject = (*this)[idx].mSubjectIdx;

	 long   SubjectLength = Sources[currentSubject].size();
	 int    IndividualPlotLength = (int)(PlotLengthScale*SubjectLength);
	 double PlotRatio = (double)IndividualPlotLength/SubjectLength;

	 bool bSameSubject = ((*this)[idx].mSubjectIdx == currentSubject);
    while(bSameSubject){
		int PlotLoc = (int) ((*this)[idx].mSubjectLocation * PlotRatio);
		// Only want to record the hits above some cutoff
		//if ((*this)[idx].mIdentitiesNum >= cutoff){
		if (1){
		  // sanity check
		  if (PlotLoc < PlotLength){
			 PlotHitBins[PlotLoc]++;
		  }else{
			 cout<<"PlotLoc out of bounds:"<<PlotLoc;
			 cout<<" PlotLength="<<PlotLength<<endl;
			 cout<<"SubjectLength="<<SubjectLength<<endl;
			 cout<<"SubjectLocation="<<(*this)[idx].mSubjectLocation<<endl;
			 cout<<"QueryLocation="<<(*this)[idx].mQueryLocation<<endl;
		  }
		}
		idx++;

		// Some compilers produce code that crashes when you even try to 
		// reference something beyond an array range, thus the importance
		// of ensuring that (*this)[] is never referenced with an idx  
		// which is out of range. 
		if (idx < this->size()){
		  bSameSubject = ((*this)[idx].mSubjectIdx == currentSubject);
		}else{
		  bSameSubject = false;
		}
	 }

	 // Now actually display the binned hits as some kind of plot thingy... 
	 PrintBinnedPlotHits(fout,Sources[currentSubject].mDescription,
								SubjectLength,(int*)PlotHitBins,
								IndividualPlotLength,PlotLength);
	 	 
	 ZeroArray(PlotHitBins,PlotLength);
	 
  }  
  delete [] PlotHitBins;
}

//---------------------------------------------------------------------------
// * PrintLocationPlot
//---------------------------------------------------------------------------
//
void
SearchResultVec::PrintLocationPlot(ostream &fout,int PlotLength,int cutoff,
											  Sequence &Source)
{
  int *PlotHitBins = new int[PlotLength];

  ZeroArray(PlotHitBins,PlotLength);

  // Sort the list into something easy to work with. 
  sort(this->begin(),this->end(),cmpByQueryLocation);
  stable_sort(this->begin(),this->end(),cmpBySubjectIdx);
  
  // Only the longest subject will take up all PlotLength spaces
  // Shorter subjects will be, um, shorter. 
  long maxSubjectSize = Source.size();
  double PlotLengthScale = (double)(PlotLength-1)/(double)maxSubjectSize;

  // while there are some results not plotted
  int idx = 0;
  while(idx < this->size()){
	 
	 // for each particular subject name, plot that subject.  Each subject, 
	 // being different lengths, will have it's own scaling factors.  
	 int currentSubject = (*this)[idx].mSubjectIdx;

	 long SubjectLength = Source.size();
	 int    IndividualPlotLength = (int)(PlotLengthScale*SubjectLength);
	 double PlotRatio = (double)IndividualPlotLength/SubjectLength;

	 bool bSameSubject = ((*this)[idx].mSubjectIdx == currentSubject);
    while(bSameSubject){
		int PlotLoc = (int) ((*this)[idx].mSubjectLocation * PlotRatio);
		// Only want to record the hits above some cutoff
		//if ((*this)[idx].mIdentitiesNum >= cutoff){
		if(1){
		  // sanity check
		  if (PlotLoc < PlotLength){
			 PlotHitBins[PlotLoc]++;
		  }else{
			 cout<<"PlotLoc out of bounds:"<<PlotLoc;
			 cout<<" PlotLength="<<PlotLength<<endl;
			 cout<<"SubjectLength="<<SubjectLength<<endl;
			 cout<<"SubjectLocation="<<(*this)[idx].mSubjectLocation<<endl;
			 cout<<"QueryLocation="<<(*this)[idx].mQueryLocation<<endl;
		  }
		}
		idx++;

		// Some compilers produce code that crashes when you even try to 
		// reference something beyond an array range, thus the importance
		// of ensuring that (*this)[] is never referenced with an idx  
		// which is out of range. 
		if (idx < this->size()){
		  bSameSubject = ((*this)[idx].mSubjectIdx == currentSubject);
		}else{
		  bSameSubject = false;
		}
	 }

	 // Now actually display the binned hits as some kind of plot thingy... 
	 PrintBinnedPlotHits(fout,Source.mDescription,
								SubjectLength,(int*)PlotHitBins,
								IndividualPlotLength,PlotLength);
	 	 
	 ZeroArray(PlotHitBins,PlotLength);
	 
  }  
  delete [] PlotHitBins;
}


//---------------------------------------------------------------------------
// * GetPrintSymbol
//---------------------------------------------------------------------------
//
char
SearchResultVec::GetPrintSymbol(int size)
{
  char buf[50];
  if (size == 0){
	 return('_');
  }else{
	 if (size < 10){
		return('#');
	 }else{
		if (size<20){
		  return('.');
		}else{
		  if (size <30){
			 return('o');
		  }else{
			 return('O');
		  }
		}
	 }
  }
}

//---------------------------------------------------------------------------
// * PrintBinnedPlotHits
//---------------------------------------------------------------------------
//
void
SearchResultVec::PrintBinnedPlotHits(ostream &fout,string &Name,
												long Length,int* PlotBins,int PlotLength,
												int MaxPlotLength)
{
  PrintSpacePaddedString(fout,11,Name.substr(0,9));
  fout<<"\t";
  for(int i = 0;i< PlotLength;i++){
	 char symbol = GetPrintSymbol(PlotBins[i]);
	 fout<<symbol;
  }
  fout<<"\t";
  PrintSpacePaddedNum(fout,MaxPlotLength-PlotLength+5,Length);
  fout<<endl;
}

